package org.flexthinker.binding.value.support
{
    import mx.collections.ArrayCollection;
    import mx.events.CollectionEvent;
    import mx.events.PropertyChangeEvent;

    import org.hamcrest.assertThat;
    import org.hamcrest.core.throws;
    import org.hamcrest.object.equalTo;
    import org.hamcrest.object.instanceOf;

    /**
     *
     */
    public class PropertyChangePublisherImplTest
    {
        private var property:PropertyChangePublisherImpl;
        private var mockBean:MockBean = new MockBean();
        private var listener:PropertyChangeListenerCounter;

        public function PropertyChangePublisherImplTest()
        {
        }

        [Before]
        public function setUp():void
        {
            listener = new PropertyChangeListenerCounter();
        }

        [Test]
        public function testPropertyChangeListener():void
        {
            property = new PropertyChangePublisherImpl(mockBean, "bindableProperty");
            property.addPropertyChangeListener(listener);
            assertThat("PropertyChangePublished should have been listening", property.isListening, equalTo(true));
            assertThat("Listener should have not been called", listener.counter, equalTo(0));

            mockBean.bindableProperty = "HELLO BINDING";
            assertThat("Listener should have been called", listener.counter, equalTo(1));

            mockBean.bindableProperty = "HELLO AGAIN";
            assertThat("Listener should have been called", listener.counter, equalTo(2));

            property.removeAllListeners();
            assertThat("listening didn't stop.", property.isListening, equalTo(false));
            mockBean.bindableProperty = "NO UPDATE";
            assertThat("Listener shouldn't have been called", listener.counter, equalTo(2));

        }

        [Test]
        public function testPropertyChangeOnCustomBindableProperty():void
        {
            property = new PropertyChangePublisherImpl(mockBean, "bindablePropertyWithCustomEvent");
            property.addPropertyChangeListener(listener);

            property.addPropertyChangeListener(
                    new DelegatingPropertyChangeListener(
                            function(event:PropertyChangeEvent):void
                            {
                                assertThat(event.oldValue, equalTo(null));
                                assertThat(event.newValue, equalTo("HELLO BINDING"));
                            }));

            assertThat("PropertyChangePublished should have been listening", property.isListening, equalTo(true));
            assertThat("Listener should have not been called", listener.counter, equalTo(0));
            mockBean.bindablePropertyWithCustomEvent = "HELLO BINDING";
            assertThat("Listener should have been called", listener.counter, equalTo(1));

            property.removeAllListeners();
            property.addPropertyChangeListener(listener);
            mockBean.bindablePropertyWithCustomEvent = "HELLO AGAIN";
            assertThat("Listener should have been called", listener.counter, equalTo(2));

            property.removePropertyChangeListener(listener);
            assertThat("listening didn't stop.", property.isListening, equalTo(false));
            mockBean.bindablePropertyWithCustomEvent = "NO UPDATE";
            assertThat("Listener shouldn't have been called", listener.counter, equalTo(2));
        }


        [Test]
        public function testCantListenToNonBindableProperties():void
        {
            property = new PropertyChangePublisherImpl(mockBean, "unbindableString");
            property.addPropertyChangeListener(listener);
            assertThat("Shouldn't have be listening for changes on a property not decorated with [Bindable] metadata",
                    property.isListening, equalTo(false));
            mockBean.unbindableString = "noBind";
            assertThat("listened shouldn't have been called", listener.counter, equalTo(0));
        }

        [Test]
        public function testPropertyChangeWithLists():void
        {
            property = new PropertyChangePublisherImpl(mockBean, "simpleList");
            property.addPropertyChangeListener(listener);
            assertThat("PropertyChangePublished should have been listening", property.isListening, equalTo(true));
            assertThat("Listener should have not been called", listener.counter, equalTo(0));

            mockBean.simpleList = new ArrayCollection([1,2,3]);
            mockBean.simpleList.addEventListener(CollectionEvent.COLLECTION_CHANGE,
                    function(event:CollectionEvent):void{});

            assertThat("Listener should have been called", listener.counter, equalTo(1));

            mockBean.simpleList.addItem(4);
            assertThat("Listener should have been called when collection changed", listener.counter, equalTo(2));

            mockBean.simpleList = new ArrayCollection([1,2]);
            mockBean.simpleList.removeItemAt(0);
            assertThat("Listener should have been called when collection changed", listener.counter, equalTo(4));

            property.removeAllListeners();
            mockBean.simpleList.addItem(5);
            assertThat("listening didn't stop.", property.isListening, equalTo(false));
            assertThat("Listener should have not been called", listener.counter, equalTo(4));
        }

        [Test]
        public function testSetHost():void
        {
            property = new PropertyChangePublisherImpl(mockBean, "bindableProperty");
            property.addPropertyChangeListener(listener);

            var newBean:MockBean = new MockBean();

            mockBean.bindableProperty = "A";
            assertThat("Listener should have been called", listener.counter, equalTo(1));

            newBean.bindableProperty = "B";

            property.setHost( newBean );
            assertThat("Listener should have been called when host has changed", listener.counter, equalTo(2));

            property.setHost(newBean);
            assertThat("Listener shouldn't be called on same host", listener.counter, equalTo(2));

            assertThat("Shouldn't accept new values as host",
                    function ():void
                    {
                        property.setHost(null)
                    },
                    throws(instanceOf(ArgumentError)));
        }


    }
}